﻿using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;

namespace Hont.BlazingNodePackage
{
    public partial class BlazingNodeEditor_View
    {
        public struct PortLink
        {
            public NodeVO A;
            public bool AIsOut;
            public int AIndex;

            public NodeVO B;
            public int BIndex;
        }

        const float PORT_SIZE_X = 15;
        const float PORT_SIZE_Y = 15;
        const float PORT_SPACING = 2f;
        const float PORT_HEIGHT = PORT_SIZE_Y + PORT_SPACING;
        const float PORT_NAME_WIDTH = 110f;
        const float NODE_BASE_HEIGHT = 24f;
        const float NODE_BASE_WIDTH = 100f;

        List<NodeVO> mSelectionNodeList;

        Rect mSelectionBoundingRect;

        Vector2 mBeginDragNodePosition;
        List<Vector2> mBeginDragNodePositionList;

        Vector2 mCacheNodeGraphScrollPosition;

        Vector2 mNodeGraphScrollPosition;
        Vector2 mNodeGraphHoldOffset;
        int mNodeGraphHoldNodeIndex;

        Vector2 mBoundingBoxBeginPosition;
        bool mRefreshBoundingBox;

        NodeVO mOperationNode;
        bool mIsGridNodeOperationEnable;
        GenericMenu mGridNodeOperationGenericMenu;

        object mBeginPreviewPort_LinkHost;
        int mBeginPreviewPort_LinkIndex;
        int mBeginPreviewPort_CacheLinkIndex;
        bool mBeginPreviewPort_IsOutPort;

        Vector2 mPreviewPortLinkA;
        Vector2 mPreviewPortLinkB;

        GraphVO mGraphVO;
        InternalPortLinkVO[] mInternalPortLinkVOArray;

        Action TryApplyPreviewPortLink_RunInLOPActionCB;

        Rect NodeGraphRect { get { return new Rect(LeftContentPanelWidth + MID_SPACING, ContentPanelOriginY, RightContentPanelWidth, Screen.height - ContentPanelOriginY - 27); } }
        Rect ScrollRect { get { return new Rect(NodeGraphRect.x + 6, NodeGraphRect.y + 2, NodeGraphRect.width - 4, NodeGraphRect.height - 4); } }
        Rect ViewRect { get { return new Rect(0, 0, 10000, 10000); } }
        Rect CurrentGraphAreaRect { get { return new Rect(ScrollRect.x + mNodeGraphScrollPosition.x, ScrollRect.y + mNodeGraphScrollPosition.y, ScrollRect.width, ScrollRect.height); } }


        public Vector2 GetInNodeDefaultPosition()
        {
            return new Vector2(140, 140);
        }

        public Vector2 GetOutNodeDefaultPosition()
        {
            return new Vector2(450, 140);
        }

        public void ResetViewRectScroll()
        {
            mNodeGraphScrollPosition = Vector3.zero;
        }

        void InitNodeGraph()
        {
            mGraphVO = new GraphVO()
            {
                NodeArray = new NodeVO[0],
                PortLinkArray = new PortLinkVO[0]
            };

            mSelectionNodeList = new List<NodeVO>();
            mBeginDragNodePositionList = new List<Vector2>();
            mInternalPortLinkVOArray = new InternalPortLinkVO[0];
            mBeginPreviewPort_LinkIndex = -1;
            mNodeGraphHoldNodeIndex = -1;
        }

        void Update_NodeGraphVO(GraphVO graphVO)
        {
            mGraphVO = graphVO;

            var internalPortLinkList = new List<InternalPortLinkVO>();

            for (int i = 0; i < mGraphVO.PortLinkArray.Length; i++)
            {
                var item = mGraphVO.PortLinkArray[i];

                var nodeA = mGraphVO.NodeArray.FirstOrDefault(m => m.Host == item.HostA);
                var nodeB = mGraphVO.NodeArray.FirstOrDefault(m => m.Host == item.HostB);

                internalPortLinkList.Add(new InternalPortLinkVO()
                {
                    NodeA = nodeA,
                    NodeB = nodeB,
                    NodeAIndex = item.NodeAIndex,
                    NodeBIndex = item.NodeBIndex,
                });
            }

            mInternalPortLinkVOArray = internalPortLinkList.ToArray();

            if (mSelectionNodeList.Count > 0)
            {
                for (int i = 0; i < mGraphVO.NodeArray.Length; i++)
                {
                    var item = mGraphVO.NodeArray[i];

                    for (int j = 0; j < mSelectionNodeList.Count; j++)
                    {
                        if (item.Host == mSelectionNodeList[j].Host)
                        {
                            mSelectionNodeList[j] = item;
                            break;
                        }
                    }
                }
            }
        }

        void Refresh_NodeGraph()
        {
            if (Event.current.type == EventType.Layout || Event.current.type == EventType.Repaint)
            {
                if (TryApplyPreviewPortLink_RunInLOPActionCB != null)
                {
                    TryApplyPreviewPortLink_RunInLOPActionCB();
                    TryApplyPreviewPortLink_RunInLOPActionCB = null;
                }
            }

            if (CurrentGraphAreaRect.Contains(mMousePosition) && mIsMouseDownOnce)
                GUI.FocusControl("");

            Refresh_NodeGraphGrid();

            Refresh_GraphScrollInput();

            mNodeGraphScrollPosition = GUI.BeginScrollView(ScrollRect, mNodeGraphScrollPosition, ViewRect, true, true);

            Refresh_DraggingObjectPreview();
            Refresh_NodeGraphNodes();
            Refresh_NodeGraphPortLinks();
            Refresh_PreviewPortLink();
            Refresh_SelectionBoundingBox();
            Refresh_GridNodeOperationMenu();

            //Debug.
            //GUI.Box(new Rect(mPreviewPortLinkA.x, mPreviewPortLinkA.y, 20, 20), "A");
            //GUI.Box(new Rect(mPreviewPortLinkB.x, mPreviewPortLinkB.y, 20, 20), "B");

            GUI.EndScrollView();

            Refresh_NodeInput();

            Refresh_Thumbnail();

            if (mIsMouseUp || !mIsMouseDown)
            {
                if (mNodeGraphHoldNodeIndex != -1)
                {
                    mNodeGraphHoldNodeIndex = -1;
                }

                if (mBeginPreviewPort_LinkIndex != -1)
                {
                    mBeginPreviewPort_CacheLinkIndex = mBeginPreviewPort_LinkIndex;
                    TryApplyPreviewPortLink_RunInLOPActionCB = TryApplyPreviewPortLink;
                    mBeginPreviewPort_LinkIndex = -1;
                }
            }
        }

        void Refresh_NodeGraphGrid()
        {
            var scroll = new Vector2();
            scroll.x = Mathf.Max(0, mNodeGraphScrollPosition.x);
            scroll.y = Mathf.Max(0, mNodeGraphScrollPosition.y);

            var drawRect = new Rect(NodeGraphRect.x + 6, NodeGraphRect.y + 2, NodeGraphRect.width - 4, NodeGraphRect.height - 4);

            GL.Begin(GL.LINES);
            EditorAssetsCache.GLMaterial.SetPass(0);
            DrawGridLines(drawRect, 20f, -scroll);
            GL.End();
        }

        void DrawGridLines(Rect graphRect, float gridSize, Vector2 offset)
        {
            float num = graphRect.x + offset.x;
            if (offset.x < 0f)
            {
                num += gridSize;
            }
            for (float num2 = num; num2 < graphRect.x + graphRect.width; num2 += gridSize)
            {
                DrawLine(graphRect, new Vector2(num2, graphRect.y), new Vector2(num2, graphRect.yMax));
            }

            float num3 = graphRect.y + offset.y;
            if (offset.y < 0f)
            {
                num3 += gridSize;
            }
            for (float num4 = num3; num4 < graphRect.yMax; num4 += gridSize)
            {
                DrawLine(graphRect, new Vector2(graphRect.x, num4), new Vector2(graphRect.xMax, num4));
            }
        }

        void DrawLine(Rect rect, Vector2 p1, Vector2 p2)
        {
            if (p1.x < rect.xMin || p2.x < rect.xMin) return;
            if (p1.x > rect.xMax || p2.x > rect.xMax) return;

            if (p1.y < rect.yMin || p2.y < rect.yMin) return;
            if (p1.y > rect.yMax || p2.y > rect.yMax) return;

            GL.Vertex(p1);
            GL.Vertex(p2);
        }

        void Refresh_NodeGraphNodes()
        {
            for (int i = 0; i < mGraphVO.NodeArray.Length; i++)
            {
                var item = mGraphVO.NodeArray[i];

                if (mNodeGraphHoldNodeIndex == i
                    && mNodeGraphHoldNodeIndex < mGraphVO.NodeArray.Length - 1)
                {
                    var lastIndex = mGraphVO.NodeArray.Length - 1;
                    var tmp = mGraphVO.NodeArray[lastIndex];
                    mGraphVO.NodeArray[lastIndex] = mGraphVO.NodeArray[i];
                    mGraphVO.NodeArray[i] = tmp;
                    mNodeGraphHoldNodeIndex = lastIndex;
                    break;
                }//depth reorder.

                var isSelection = mSelectionNodeList.Contains(item);

                if (isSelection
                   && Event.current.keyCode == KeyCode.Delete
                   && Event.current.type == EventType.KeyUp)
                {
                    if (item.OnNodeDeleteCB != null)
                        item.OnNodeDeleteCB(mSelectionNodeList);

                    mSelectionNodeList.Clear();

                    break;
                }//Delete node.

                if (item.OnEditorUpdateCB != null)
                    item.OnEditorUpdateCB();

                var nodeRect = GetNodeRect(item);

                Refresh_NodeGUI(nodeRect, item, isSelection);

                Refresh_PortGUI(item, nodeRect);

                Refresh_SingleNodeInput(item, nodeRect, i);

                if (!string.IsNullOrEmpty(item.Remark))
                {
                    var textRect = GUILayoutUtility.GetRect(new GUIContent(item.Remark), GUI.skin.label, GUILayout.MaxWidth(40));
                    var tempColor = GUI.color;
                    GUI.color = Color.Lerp(Color.green, new Color(tempColor.r, tempColor.g, tempColor.b, 0.2f), 0.4f);
                    GUI.Box(new Rect(nodeRect.x, nodeRect.yMax, nodeRect.width, textRect.height), "//" + item.Remark, GUI.skin.label);
                    GUI.color = tempColor;
                }//Draw Remark.
            }
        }

        void Refresh_NodeGraphPortLinks()
        {
            for (int i = 0; i < mInternalPortLinkVOArray.Length; i++)
            {
                var item = mInternalPortLinkVOArray[i];

                var nodeARect = GetNodeRect(item.NodeA);
                var nodeBRect = GetNodeRect(item.NodeB);

                var portARect = GetPortRect(item.NodeA, nodeARect, item.NodeAIndex, false);
                var portBRect = GetPortRect(item.NodeB, nodeBRect, item.NodeBIndex, true);

                var portAPosition = new Vector2(portARect.xMax, portARect.center.y);
                var portBPosition = new Vector2(portBRect.x, portBRect.center.y);

                BlazingMonoEditorHelper.DrawBezier(portAPosition, portBPosition, false);
                BlazingMonoEditorHelper.DrawBezierDirection(portAPosition, portBPosition, (float)EditorApplication.timeSinceStartup, Color.red, false);
            }
        }

        void Refresh_PreviewPortLink()
        {
            if (mBeginPreviewPort_LinkIndex == -1) return;

            var leftArcOrRight = mPreviewPortLinkA.x > mPreviewPortLinkB.x;
            BlazingMonoEditorHelper.DrawBezier(mPreviewPortLinkA, mPreviewPortLinkB, leftArcOrRight);
        }

        Rect GetPortRect(NodeVO node, Rect nodeRect, int index, bool inOrOut)
        {
            if (node.CustomGUINode != null)
                return node.CustomGUINode.GetPortRect(nodeRect, node.Host as Node, index, inOrOut);

            if (inOrOut)
                return new Rect(nodeRect.x, nodeRect.y + 20 + PORT_HEIGHT * index, PORT_SIZE_X, PORT_SIZE_Y);
            else
                return new Rect(nodeRect.xMax - PORT_SIZE_X, nodeRect.y + 20 + PORT_HEIGHT * index, PORT_SIZE_X, PORT_SIZE_Y);
        }

        Rect GetNodeRect(NodeVO node)
        {
            if (node.CustomGUINode != null)
                return node.CustomGUINode.GetNodeRect(node.Host as Node);

            var rect = new Rect(Vector2.zero, node.Size);
            rect.center = node.Position;

            return rect;
        }

        void Refresh_SelectionBoundingBox()
        {
            if (mLastMouseButton == 2) return;
            if (mIsDraggingFunctionItem) return;

            var innerGraphRect = NodeGraphRect;
            innerGraphRect.size *= 0.96f;
            if (!innerGraphRect.Contains(mMousePosition)) return;

            if (mNodeGraphHoldNodeIndex != -1) return;
            if (mBeginPreviewPort_LinkIndex != -1) return;
            if (mIsLibraryEnable) return;

            if (mIsMouseDownOnce)
            {
                if (!mRefreshBoundingBox)
                {
                    mRefreshBoundingBox = true;

                    mBoundingBoxBeginPosition = mNodeGraphScrollPosition + mMousePosition;
                }
            }

            if (mIsMouseUpOnce)
            {
                mRefreshBoundingBox = false;
            }

            if (mRefreshBoundingBox)
            {
                mSelectionBoundingRect = new Rect(mBoundingBoxBeginPosition.x, mBoundingBoxBeginPosition.y, mMousePosition.x, mMousePosition.y);
                mSelectionBoundingRect.min = new Vector2(mBoundingBoxBeginPosition.x, mBoundingBoxBeginPosition.y) - NodeGraphRect.min;
                mSelectionBoundingRect.max = mNodeGraphScrollPosition + new Vector2(mMousePosition.x, mMousePosition.y) - NodeGraphRect.min;

                var oldColor = Handles.color;
                Handles.color = Color.black;
                Handles.DrawAAPolyLine(3, new Vector3[]
                {
                    new Vector3(mSelectionBoundingRect.min.x, mSelectionBoundingRect.min.y, 0),
                    new Vector3(mSelectionBoundingRect.min.x, mSelectionBoundingRect.max.y, 0),
                    new Vector3(mSelectionBoundingRect.max.x, mSelectionBoundingRect.max.y, 0),
                    new Vector3(mSelectionBoundingRect.max.x, mSelectionBoundingRect.min.y, 0),
                    new Vector3(mSelectionBoundingRect.min.x, mSelectionBoundingRect.min.y, 0),
                });
                Handles.color = oldColor;
            }
        }

        void Refresh_GridNodeOperationMenu()
        {
            if (mGridNodeOperationGenericMenu != null && mIsGridNodeOperationEnable)
            {
                mIsGridNodeOperationEnable = false;
                mGridNodeOperationGenericMenu = null;
            }

            if (mIsGridNodeOperationEnable && mGridNodeOperationGenericMenu == null)
            {
                mGridNodeOperationGenericMenu = new GenericMenu();

                mGridNodeOperationGenericMenu.AddItem(new GUIContent("Delete")
                    , false
                    , new GenericMenu.MenuFunction(() =>
                    {
                        if (mOperationNode.OnNodeDeleteCB != null)
                            mOperationNode.OnNodeDeleteCB(mSelectionNodeList);

                        mSelectionNodeList.Clear();
                    }));

                mGridNodeOperationGenericMenu.AddItem(new GUIContent("Remark")
                   , false
                   , new GenericMenu.MenuFunction(() =>
                   {
                       if (mOperationNode.OnNodeRemarkCB != null)
                           mOperationNode.OnNodeRemarkCB(mOperationNode);
                   }));

                mGridNodeOperationGenericMenu.ShowAsContext();
            }
        }

        void Refresh_NodeGUI(Rect rect, NodeVO node, bool isSelection)
        {
            var color = GUI.color;

            color = node.RootNodeHighligh ? BlazingMonoEditorHelper.RootNodeColor : color;
            color = node.CopyHightlight ? BlazingMonoEditorHelper.CopyHightlightColor : color;
            color = node.NodeIsArgumentBind ? BlazingMonoEditorHelper.ArgumentBindNodeColor : color;

            color = isSelection ? BlazingMonoEditorHelper.SelectionNodeColor : color;

            if (node.CustomGUINode != null)
            {
                node.CustomGUINode.DrawNode(node.Host as INode, isSelection, color);
                return;
            }

            var oldColor = GUI.color;

            GUI.color = color;

            GUI.Box(rect, node.Name.ValueFunc());

            GUI.color = oldColor;
        }

        void Refresh_NodeInput()
        {
            if (!NodeGraphRect.Contains(mMouseDownPosition)) return;
            if (!NodeGraphRect.Contains(mMousePosition)) return;
            if (mIsMouseDown) return;

            if (mCommandChange && mStableCommandName == "Cut")//Cut.
            {
                if (mGraphVO.NodeGraph_OnCutNodeCB != null)
                    mGraphVO.NodeGraph_OnCutNodeCB(mSelectionNodeList);
            }

            if (mCommandChange && mStableCommandName == "Copy")//Copy.
            {
                if (mGraphVO.NodeGraph_OnCopyNodeCB != null)
                    mGraphVO.NodeGraph_OnCopyNodeCB(mSelectionNodeList);
            }

            else if (mCommandChange && mStableCommandName == "Paste")//Paste.
            {
                var localMousePosition = mNodeGraphScrollPosition + (mMousePosition - NodeGraphRect.min);

                if (mGraphVO.NodeGraph_OnPasteNodeCB != null)
                    mGraphVO.NodeGraph_OnPasteNodeCB(localMousePosition);

                mSelectionNodeList.Clear();
            }
        }

        void Refresh_SingleNodeInput(NodeVO node, Rect rect, int index)
        {
            if (mLastMouseButton == 2) return;
            if (mIsDraggingFunctionItem) return;
            if (!NodeGraphRect.Contains(mMousePosition)) return;
            if (mIsLibraryEnable) return;
            if (mBeginPreviewPort_LinkIndex != -1) return;

            const float DRAG_ERROR = 3f;
            var localMousePosition = mNodeGraphScrollPosition + (mMousePosition - NodeGraphRect.min);

            if (mRefreshBoundingBox
                && mNodeGraphHoldNodeIndex == -1)//Bounding Box
            {
                var nodeRect = GetNodeRect(node);

                if (mSelectionBoundingRect.Overlaps(nodeRect, true))
                {
                    if (!mSelectionNodeList.Contains(node))
                        mSelectionNodeList.Add(node);
                }
                else if (!mIsHoldShift)
                {
                    mSelectionNodeList.Remove(node);
                }
            }

            else if (rect.Contains(localMousePosition)
              && mIsMouseDoubleClicke
              && mLastMouseButton == 0
              && (node.Position - mBeginDragNodePosition).magnitude < DRAG_ERROR)//Double Click
            {
                mSelectionNodeList.Clear();
                GUI.FocusControl("");

                if (mGraphVO.NodeGraph_OnDoubleClickedNodeCB != null)
                    mGraphVO.NodeGraph_OnDoubleClickedNodeCB(node);
            }

            else if (rect.Contains(localMousePosition) && RightButtonClicked)//Right Menu
            {
                mIsGridNodeOperationEnable = true;
                mOperationNode = node;
                GUI.FocusControl("");
            }

            else if (rect.Contains(localMousePosition)
                  && mNodeGraphHoldNodeIndex == -1
                  && mIsMouseDown)//DragBegin
            {
                mNodeGraphHoldNodeIndex = index;
                mNodeGraphHoldOffset = rect.center - localMousePosition;
                mBeginDragNodePosition = node.Position;

                if (!mSelectionNodeList.Contains(node))
                {
                    mSelectionNodeList.Clear();
                    mSelectionNodeList.Add(node);
                }

                mBeginDragNodePositionList.Clear();
                mBeginDragNodePositionList.AddRange(mSelectionNodeList.Select(m => m.Position).ToArray());
                GUI.FocusControl("");
            }

            else if (mNodeGraphHoldNodeIndex == index && mIsMouseDown)//Dragging
            {
                if (mGraphVO.NodeGraph_OnDraggingNodeCB != null)
                {
                    for (int i = 0; i < mSelectionNodeList.Count; i++)
                    {
                        var item = mSelectionNodeList[i];

                        var beginPosition = mBeginDragNodePosition;
                        var draggingPosition = localMousePosition + mNodeGraphHoldOffset;

                        var newPosition = mBeginDragNodePositionList[i] + (draggingPosition - beginPosition);
                        mGraphVO.NodeGraph_OnDraggingNodeCB(item, newPosition);
                    }
                }
            }

            else if (NodeGraphRect.Contains(mMousePosition)
                && mNodeGraphHoldNodeIndex == index
                && mIsHoldCtrl
                && mIsMouseUpOnce)//Drop Duplicate.
            {
                for (int i = 0; i < mSelectionNodeList.Count; i++)
                {
                    var item = mSelectionNodeList[i];

                    var beginPosition = mBeginDragNodePosition;
                    var draggingPosition = localMousePosition + mNodeGraphHoldOffset;

                    var newPosition = mBeginDragNodePositionList[i] + (draggingPosition - beginPosition);

                    mGraphVO.NodeGraph_OnDuplicateNodeCB(item, newPosition);
                    mGraphVO.NodeGraph_OnDraggingNodeCB(item, mBeginDragNodePositionList[i]);
                }
            }

            else if (rect.Contains(localMousePosition)
                && mNodeGraphHoldNodeIndex == index
                && mIsMouseUpOnce
                && (node.Position - mBeginDragNodePosition).magnitude < DRAG_ERROR)//Click
            {
                mSelectionNodeList.Clear();
                mSelectionNodeList.Add(node);
                GUI.FocusControl("");
            }
        }

        void Refresh_PortGUI(NodeVO node, Rect nodeRect)
        {
            if (node.CustomGUINode != null)
            {
                var host = node.Host as INode;
                for (int i = 0; i < node.InPortNameArray.Length; i++)
                {
                    var portRect = GetPortRect(node, nodeRect, i, true);
                    node.CustomGUINode.DrawPort(nodeRect, host, i, true);
                    Refresh_PortInput(node, portRect, false, i);
                }

                for (int i = 0; i < node.OutPortNameArray.Length; i++)
                {
                    var portRect = GetPortRect(node, nodeRect, i, false);
                    node.CustomGUINode.DrawPort(nodeRect, host, i, false);
                    Refresh_PortInput(node, portRect, true, i);
                }

                return;
            }

            var lastY = 20f;
            for (int i = 0; i < node.InPortNameArray.Length; i++)
            {
                var inPortName = node.InPortNameArray[i].ValueFunc();

                var inPortRect = GetPortRect(node, nodeRect, i, true);

                var textSelfWidth = GUI.skin.box.CalcSize(new GUIContent(inPortName)).x;
                var portTextWidth = Mathf.Max(PORT_NAME_WIDTH, textSelfWidth);
                var inPortNameRect = new Rect(inPortRect.xMax, nodeRect.y + lastY, portTextWidth, PORT_SIZE_Y);
                GUI.Box(inPortRect, "");
                GUI.Label(inPortNameRect, inPortName);

                GUI.Box(inPortRect, "");
                GUI.Label(inPortNameRect, inPortName);

                Refresh_PortInput(node, inPortRect, false, i);

                lastY += PORT_HEIGHT;
            }

            lastY = 20f;
            for (int i = 0; i < node.OutPortNameArray.Length; i++)
            {
                var outPortName = node.OutPortNameArray[i].ValueFunc();
                var textSelfWidth = GUI.skin.box.CalcSize(new GUIContent(outPortName)).x;
                var portTextWidth = Mathf.Min(PORT_NAME_WIDTH, textSelfWidth);

                var outPortRect = GetPortRect(node, nodeRect, i, false);
                var outPortNameRect = new Rect(outPortRect.xMin - portTextWidth, nodeRect.y + lastY, PORT_NAME_WIDTH, PORT_HEIGHT);

                GUI.Box(outPortRect, "");
                GUI.Label(outPortNameRect, outPortName);

                Refresh_PortInput(node, outPortRect, true, i);

                lastY += PORT_HEIGHT;
            }
        }

        void Refresh_PortInput(NodeVO node, Rect portRect, bool isOutPort, int index)
        {
            if (mIsLibraryEnable) return;
            if (mLastMouseButton == 2) return;

            var error = new Vector2(6, 4);
            var localMousePosition = mNodeGraphScrollPosition + (mMousePosition - NodeGraphRect.min) - error;

            if (portRect.Contains(localMousePosition)
                && mIsHoldAlt
                && mIsMouseDownOnce)//Delete Node.
            {
                var targetPortLink = default(InternalPortLinkVO);

                if (isOutPort)
                {
                    targetPortLink = mInternalPortLinkVOArray.FirstOrDefault(m => m.NodeA == node && m.NodeAIndex == index);
                }
                else
                {
                    targetPortLink = mInternalPortLinkVOArray.FirstOrDefault(m => m.NodeB == node && m.NodeBIndex == index);
                }

                if (mGraphVO.NodeGraph_OnPortDeleteCB != null)
                    mGraphVO.NodeGraph_OnPortDeleteCB(new PortLink()
                    {
                        A = targetPortLink.NodeA,
                        AIndex = targetPortLink.NodeAIndex,
                        B = targetPortLink.NodeB,
                        BIndex = targetPortLink.NodeBIndex,
                        AIsOut = isOutPort,
                    });

                return;
            }

            if (portRect.Contains(localMousePosition)
                && mBeginPreviewPort_LinkIndex == -1
                && mIsMouseDownOnce)//Begin Connect Line.
            {
                mPreviewPortLinkA = portRect.center;
                mBeginPreviewPort_LinkHost = node.Host;
                mBeginPreviewPort_LinkIndex = index;

                mBeginPreviewPort_IsOutPort = isOutPort;
            }

            if (mBeginPreviewPort_LinkIndex == index
                && mBeginPreviewPort_IsOutPort == isOutPort
                && mIsMouseDown)
            {
                mPreviewPortLinkB = localMousePosition;
            }
        }

        void Refresh_GraphScrollInput()
        {
            if (!mIsMouseDown) return;
            if (mLastMouseButton != 2) return;

            if (mIsMouseDownOnce)
                mCacheNodeGraphScrollPosition = mNodeGraphScrollPosition;

            var relative = mMousePosition - mMouseDownPosition;

            mNodeGraphScrollPosition = Vector2.Lerp(mNodeGraphScrollPosition, mCacheNodeGraphScrollPosition - relative, 0.1f);

            EditorGUIUtility.AddCursorRect(NodeGraphRect, MouseCursor.Pan);
        }

        void Refresh_DraggingObjectPreview()
        {
            if (!NodeGraphRect.Contains(mMousePosition)
                  || mNodeGraphHoldNodeIndex == -1
                  || !mIsMouseDown
                  || !mIsHoldCtrl)
            {
                return;
            }

            var oldColor = GUI.color;

            GUI.color = new Color(oldColor.r, oldColor.g, oldColor.b, 0.3f);

            for (int i = 0; i < mSelectionNodeList.Count; i++)
            {
                var item = mSelectionNodeList[i];

                var rect = GetNodeRect(item);
                rect.center = mBeginDragNodePositionList[i];

                GUI.Box(rect, "");
            }

            GUI.color = oldColor;
        }

        void Refresh_Thumbnail()
        {
            var oldColor = GUI.color;
            var borderRect = new Rect(NodeGraphRect.max.x - 160, NodeGraphRect.max.y - 100, 140, 80);

            Func<Rect, Rect> conv = (inRect) =>
            {
                var xMinRate = Mathf.Clamp01((inRect.xMin - ViewRect.x) / ViewRect.size.x);
                var yMinRate = Mathf.Clamp01((inRect.yMin - ViewRect.y) / ViewRect.size.y);

                var xMaxRate = Mathf.Clamp01((inRect.xMax - ViewRect.x) / ViewRect.size.x);
                var yMaxRate = Mathf.Clamp01((inRect.yMax - ViewRect.y) / ViewRect.size.y);

                var finalXMin = borderRect.x + borderRect.size.x * xMinRate;
                var finalYMin = borderRect.y + borderRect.size.y * yMinRate;

                var finalXMax = borderRect.x + borderRect.size.x * xMaxRate;
                var finalYMax = borderRect.y + borderRect.size.y * yMaxRate;

                var result = new Rect();
                result.min = new Vector2(finalXMin, finalYMin);
                result.max = new Vector2(finalXMax, finalYMax);

                return result;
            };

            var currentViewRect = conv(new Rect(mNodeGraphScrollPosition.x, mNodeGraphScrollPosition.y, NodeGraphRect.width, NodeGraphRect.height));
            GUI.Box(currentViewRect, "");

            for (int i = 0; i < mGraphVO.NodeArray.Length; i++)
            {
                var item = mGraphVO.NodeArray[i];

                var nodeRect = GetNodeRect(item);

                nodeRect = conv(nodeRect);

                GUI.Box(nodeRect, "");
            }

            GUI.color = new Color(oldColor.r, oldColor.g, oldColor.b, 0.5f);
            GUI.Box(borderRect, "");
            GUI.color = oldColor;
        }

        void TryApplyPreviewPortLink()
        {
            for (int i = 0; i < mGraphVO.NodeArray.Length; i++)
            {
                var item = mGraphVO.NodeArray[i];

                var nodeRect = GetNodeRect(item);

                if (mBeginPreviewPort_IsOutPort)
                {
                    for (int j = 0; j < item.InPortNameArray.Length; j++)
                    {
                        var portRect = GetPortRect(item, nodeRect, j, true);

                        if (portRect.Contains(mPreviewPortLinkB))
                        {
                            var nodeVO = mGraphVO.NodeArray.FirstOrDefault(m => m.Host == mBeginPreviewPort_LinkHost);

                            AddPortLink(nodeVO, mBeginPreviewPort_IsOutPort, mBeginPreviewPort_CacheLinkIndex, item, j);

                            return;
                        }
                    }
                }

                if (!mBeginPreviewPort_IsOutPort)
                {
                    for (int j = 0; j < item.OutPortNameArray.Length; j++)
                    {
                        var portRect = GetPortRect(item, nodeRect, j, false);

                        //Safe Area.
                        portRect.size += Vector2.one * 4;

                        if (portRect.Contains(mPreviewPortLinkB))
                        {
                            var nodeVO = mGraphVO.NodeArray.FirstOrDefault(m => m.Host == mBeginPreviewPort_LinkHost);
                            AddPortLink(nodeVO, mBeginPreviewPort_IsOutPort, mBeginPreviewPort_CacheLinkIndex, item, j);

                            return;
                        }
                    }
                }
            }
        }

        void AddPortLink(NodeVO a, bool aIsOut, int aIndex, NodeVO b, int bIndex)
        {
            if (a == b) return;

            var newPortLink = new PortLink()
            {
                A = a,
                AIndex = aIndex,
                AIsOut = aIsOut,
                B = b,
                BIndex = bIndex,
            };

            if (mGraphVO.NodeGraph_OnNewPortLinkedCB != null)
                mGraphVO.NodeGraph_OnNewPortLinkedCB(newPortLink);
        }
    }
}
